//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
// 

#include "CustomAppLayer.h"

#include <MobilityAccess.h>

#include "CustomApplPkt_m.h"

#include <Coord.h>

using std::endl;

Define_Module(CustomAppLayer)
;

void CustomAppLayer::initialize(int stage) {
    //Registrar señales
    receivedSignal = registerSignal("received");
    positionXSignal = registerSignal("xposition");
    positionYSignal = registerSignal("yposition");
    sentSignal = registerSignal("sent");
    receivedBroadcastSignal = registerSignal("received_broadcast");

    // Inicializar variables
    packageID = 0;
    numSent = 0;
    numReceived = 0;
    numReceivedBroadcast = 0;
    WATCH(numSent);
    WATCH(numReceived);
    WATCH(packageID);
    WATCH(numReceivedBroadcast);

    MyTestAppLayer::initialize(stage);
    if (stage == 0) {
        if (hasPar("burstSize"))
            burstSize = par("burstSize");
        else
            burstSize = 3;
        if (hasPar("burstReply"))
            bSendReply = par("burstReply");
        else
            bSendReply = true;


    }
}

/**
 * Mensajes que se envía el nodo a sí mismo
 */
void CustomAppLayer::handleSelfMsg(cMessage *msg) {
    switch (msg->getKind()) {

    case SEND_BROADCAST_TIMER:
        break;

        //El mensaje POSITION_TIMER indica que es tiempo de enviar sus datos al nodo central
    case POSITION_TIMER:

        //Todos menos el nodo central (Nodo 2), envían su posición al nodo central
        if (myApplAddr() != 2) {

            //Aumentar el ID actual para asignar al paquete que se va a enviar
            packageID++;

            //Obtener datos del nodo para añadir al paquete
            double xposition = getModuleXPosition();
            double yposition = getModuleYPosition();
            double speed = getModuleSpeed();


            //Enviar paquete
            sendNodeInfo(packageID, xposition, yposition, speed, 2);

            //Actualizar GUI con número de paquetes envíados
            numSent++;
            if (ev.isGUI())
            updateDisplay();

            //Se emite una señal cada vez que se envía un paquete con su ID
            emit(sentSignal, packageID);

            //Se emite dos señales con la posicion x e y del nodo al enviar un paquete
            emit(positionXSignal, xposition);
            emit(positionYSignal, yposition);

            //Volver a iniciar el timer para enviar el siguiente paquete
            delayTimer = new cMessage("position-timer", POSITION_TIMER);
            scheduleAt(simTime() + dblrand() * 5, delayTimer);

            EV<<"I send a message";
        }

        break;

        default:
        EV <<" Unkown selfmessage! -> delete, kind: "<<msg->getKind()<<endl;
        break;
    }

}

            /**
             *Mensajes que llegan desde las capas inferiores
             */
void CustomAppLayer::handleLowerMsg(cMessage* msg) {

    //Solo es envíado por el nodo central -- Nodo 2
    if (msg->getKind() == BROADCAST_MESSAGE) {
        CustomApplPkt *m = static_cast<CustomApplPkt *>(msg);
        if (m->getSrcAddr() == 2 && myApplAddr() != 2) {
            EV<< "Receive message";
            coreEV << "Received a broadcast packet from host[" << m->getSrcAddr()
            << "] -> delete message, no reply" << endl;

            //Se emite una señal indicando que se recibió el paquete broadcast
            emit(receivedBroadcastSignal,m->getId());
            numReceivedBroadcast++;
            if (ev.isGUI())
            updateDisplay();

        }

        delete msg;
        return;

    } else {

        //Mensaje de otro nodo con sus posiciones y velocidad --> Solamente lo debe recibir el nodo principal
        if (msg->getKind() == POSITION_MESSAGE) {
            CustomApplPkt *m = static_cast<CustomApplPkt *>(msg);

            if (myApplAddr() == m->getDestAddr()) {

                EV<< "I receive a message" << m;

                //Actualizar GUI con número de paquetes recibidos
                numReceived++;
                if (ev.isGUI())
                updateDisplay();

                //Se obtiene el ID del paquete
                int packageID = m->getId();

                //Se emite una señal indicando que llegó un paquete nuevo al nodo
                emit(receivedSignal, packageID);

                //Se emiten las señales correspondientes a las posiciones del nodo al llegar un paquete
                emit(positionXSignal,getModuleXPosition());
                emit(positionYSignal,getModuleYPosition());

                //Obtener información del paquete para reenviar al resto de nodos
                double xposition = m->getXposition();
                double yposition = m->getYposition();
                double speed = m->getSpeed();
                //Enviar paquete al resto de nodos
                sendBroadcast(packageID,xposition,yposition,speed);
                //Se emite una señal cada vez que se envía un paquete con su ID
                emit(sentSignal, packageID);
                delete msg;

            }

            return;
        }
    }
    MyTestAppLayer::handleLowerMsg(msg);
}

/*
 * Obtener la posición x del módulo
 */
double CustomAppLayer::getModuleXPosition() {
    Coord c;
    double posx = 0;
    c = MobilityAccess().get(findHost())->getCurrentPosition();
    posx = c.x;
    return posx;
}

/*
 * Obtener la posición y del módulo
 */
double CustomAppLayer::getModuleYPosition() {
    Coord c;
    double posy = 0;
    c = MobilityAccess().get(findHost())->getCurrentPosition();
    posy = c.y;
    return posy;
}

/*
 * Obtener velocidad del módulo
 */
double CustomAppLayer::getModuleSpeed() {
    Coord c;
    double speed = 0;
    c = MobilityAccess().get(findHost())->getCurrentSpeed();
    speed = c.length();
    return speed;
}

/**
 * Actualizar el tag del nodo mostrando el número de paquetes recibidos y envíados
 */
void CustomAppLayer::updateDisplay() {
    char buf[40];
    sprintf(buf, "rcvd: %ld sent: %ld broadrcvd: %ld", numReceived, numSent,
            numReceivedBroadcast);
    findHost()->getDisplayString().setTagArg("t", 0, buf);
}

